#pragma once

#include <list>

namespace My {

class Event { 
public:
	virtual ~Event() { } // ensures presence of a vtable
public:
	template <class T> bool Is()
		{ return dynamic_cast<T *>(this) != 0; }
	template <class T> T & As()
		{ return * dynamic_cast<T *>(this); }
};

/**
 * Base for all classes that react on events
 */
class EventListener {
public:
	virtual void ProcessEvent(Event &) = 0;
};

struct DispatchEvent : std::unary_function<EventListener *, void> {
	Event &	event;
	DispatchEvent(Event & e) : event(e) { }
	void operator () (EventListener * ptr) { ptr->ProcessEvent(event); }
};

class EventDispatcher {
public:
	void DispatchEvent(Event &);
	void Attach(EventListener *);
	void Detach(EventListener *);
private:
	std::list<EventListener *>	m_Listeners;	
};

/**
 * Attach one object to listen events dispatched by another object
 * during lifetime of an EventSubscription
 */
class EventSubscription {
public:
	EventSubscription(EventDispatcher & d, EventListener & l) :
		dispatcher(d), listener(l) 
		{ dispatcher.Attach(&listener); }
	~EventSubscription()
		{ dispatcher.Detach(&listener); }
private:
	EventDispatcher & dispatcher;
	EventListener & listener;
};

class BroadcastEvent : public Event
{
};

class WindowEvent : public Event
{
};

class WindowCreateEvent : public WindowEvent {
};

class WindowDestroyEvent : public WindowEvent {
};

class WindowBoundsEvent : public WindowEvent {
public:
	bool	m_Update;
public:
	WindowBoundsEvent(bool update)
		: m_Update(update) { }
};

class WindowVisibleEvent : public WindowEvent {
public:
	bool	m_Visible;
public:
	WindowVisibleEvent(bool visible)
		: m_Visible(visible) { }
};

class ActivateEvent : public BroadcastEvent {
public:
	bool	m_Active;
public:
	ActivateEvent(bool active)
		: m_Active(active) { }
};

class UpdateUIEvent : public BroadcastEvent {
};

class FocusEvent : public Event {
};

class UnfocusEvent : public Event {
};

class KeyEvent : public Event {
public:
	unsigned int m_Key;
	unsigned int m_Count;
	unsigned int m_Flags;
public:
	KeyEvent(unsigned int key, unsigned int count, unsigned int flags)
		: m_Key(key), m_Count(count), m_Flags(flags) { }
};

class KeyDownEvent : public KeyEvent {
public:
	KeyDownEvent(unsigned int key, unsigned int count, unsigned int flags)
		: KeyEvent(key, count, flags) { }
};

class KeyUpEvent : public KeyEvent {
public:
	KeyUpEvent(unsigned int key, unsigned int count, unsigned int flags)
		: KeyEvent(key, count, flags) { }
};

class MouseEvent : public Event {
public:
	unsigned int	m_Flags;
	CPoint			m_Point;
public:
	MouseEvent(unsigned int flags, const CPoint & p)
		: m_Flags(flags), m_Point(p) { }
};

class LeftDoubleClickEvent : public MouseEvent {
public:
	LeftDoubleClickEvent(unsigned int flags, const CPoint & p)
		: MouseEvent(flags, p) { }
};

class LeftDownEvent : public MouseEvent {
public:
	LeftDownEvent(unsigned int flags, const CPoint & p)
		: MouseEvent(flags, p) { }
};

class LeftUpEvent : public MouseEvent {
public:
	LeftUpEvent(unsigned int flags, const CPoint & p)
		: MouseEvent(flags, p) { }
};

class MouseMoveEvent : public MouseEvent {
public:
	MouseMoveEvent(unsigned int flags, const CPoint & p)
		: MouseEvent(flags, p) { }
};

class MouseEnterEvent : public MouseEvent {
public:
	MouseEnterEvent(unsigned int flags, const CPoint & p)
		: MouseEvent(flags, p) { }
};

class MouseLeaveEvent : public Event {
};

class RightDoubleClickEvent : public MouseEvent {
public:
	RightDoubleClickEvent(unsigned int flags, const CPoint & p)
		: MouseEvent(flags, p) { }
};

class RightDownEvent : public MouseEvent {
public:
	RightDownEvent(unsigned int flags, const CPoint & p)
		: MouseEvent(flags, p) { }
};

class RightUpEvent : public MouseEvent {
public:
	RightUpEvent(unsigned int flags, const CPoint & p)
		: MouseEvent(flags, p) { }
};

class CursorEvent : public MouseEvent {
public:
	bool m_Handled;
public:
	CursorEvent(unsigned int flags, const CPoint & p)
		: MouseEvent(flags, p), m_Handled(false) { }
};

class FontEvent : public BroadcastEvent {
};

class ScrollBar;

class ScrollEvent : public Event {
public:
	ScrollBar *	m_pScrollBar;
	UINT		m_Code;
	UINT		m_Pos;
public:
	ScrollEvent(ScrollBar * ptr, UINT nCode, UINT nPos)
		: m_pScrollBar(ptr), m_Code(nCode), m_Pos(nPos) { }
};

class HorzScrollEvent : public ScrollEvent {
public:
	HorzScrollEvent(ScrollBar * ptr, UINT nCode, UINT nPos)
		: ScrollEvent(ptr, nCode, nPos) { }
};

class VertScrollEvent : public ScrollEvent {
public:
	VertScrollEvent(ScrollBar * ptr, UINT nCode, UINT nPos)
		: ScrollEvent(ptr, nCode, nPos) { }
};

class CommandEventBase : public Event {
public:
	UINT m_nID;
	bool m_Handled;
public:
	CommandEventBase(UINT nID)
		: m_nID(nID), m_Handled(false) { }
};

class CommandEvent : public CommandEventBase {
public:
	CommandEvent(UINT nID)
		: CommandEventBase(nID) { }
};

class UpdateCommandUIEvent : public CommandEventBase {
public:
	CCmdUI * m_pCmdUI;
public:
	UpdateCommandUIEvent(UINT nID, CCmdUI * p)
		: CommandEventBase(nID), m_pCmdUI(p) { }
};

} // My
